package org.nViron.Graffiti;

import instar.dg.Client.BaseClient;
import instar.dg.Client.ClientListener;
import instar.dg.Common.Error;
import instar.dg.Common.ID;
import instar.dg.Common.Position;
import instar.dg.Common.PositionListener;
import instar.dg.Common.QualifiedPosition;
import instar.dg.Debug.Debug;
import instar.dg.GraffitiCache.GraffitiCache;
import instar.dg.GraffitiCache.GraffitiCacheListener;
import instar.dg.GraffitiCache.PersistentCache;
import instar.dg.GraffitiCache.PersistentCacheMemory;
import instar.dg.Graffito.GraffitoBody;
import instar.dg.Graffito.GraffitoHeader;
import instar.dg.Peer.Ticket;
import instar.dg.Peer.TicketTimeoutListener;
import instar.dg.Positioning.PositionProvider;
import instar.dg.Profile.AddressBookListener;
import instar.dg.Profile.Friend;
import instar.dg.Profile.LoadPersonListener;
import instar.dg.Profile.Person;
import instar.dg.Protocol.Messages.WLANPosAvailable;
import instar.dg.Protocol.Requests.LoginRequest;
import instar.dg.SpatialCoding.SpatialNameList;
import instar.dg.Tiles.TilesCache;
import org.nViron.Tiles.TileSource;
import org.nViron.client.Config;
import org.nViron.nvData.nvDataSource;
import org.nViron.nvSim.nvSimulator;

/**
 *
 * @author Roland
 */
public class GraffitiSource extends nvDataSource {

    public static final byte NO_ERROR = 0x00;

    private GraffitiClient baseclient;
    private Config config;
    private ID userID;

    public GraffitiSource(nvSimulator simulator) {
        super(simulator);
        config = (Config)simulator.getConfig();
        baseclient = new GraffitiClient(this, config);
        baseclient.addClientListener(new ClientFacade());
        baseclient.addPositionListener(new PositionListenerImpl());

        PersistentCache persistent = null;
        if (config.isUsePersistentGraffitiCache()) {
            try {
                persistent = new PersistentCacheSE(baseclient, config.getCachePath(), config.getCacheNumberOfHeader(), config.getCacheNumberOfBodies());

            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } else {
            persistent = new PersistentCacheMemory(baseclient, config.getCacheNumberOfHeader(), config.getCacheNumberOfBodies());
        }
        baseclient.setGraffitiCache(new GraffitiCache(baseclient, persistent));
        baseclient.getGraffitiCache().addListener(new GraffitiFacade());

        baseclient.getProviderPeer().getTicketManager().setListener(new TimeoutFacade());
        baseclient.getMe().getProfile().getAddressBook().addAddressBookListener(new AddressbookFacade());

        baseclient.getGraffitiCache().checkGraffitiInRange();
    }

    @Override
    protected void finalize() throws Throwable {
        baseclient.stop();
        super.finalize();
    }

    @Override
    public boolean init() {
        if (baseclient.connect(config.getProviderIP(), config.getProviderPort()) != NO_ERROR) {
            return false;
        }

        baseclient.doLogin("roland", "roland");

        return true;
    }

    public void remove(ID id) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void addGraffito(GraffitoHeader gh) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void addFriend(Friend f) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void setID(ID id) {
        this.userID = id;
        ((TileSource)simulator.getMapSource()).setUserID(id);
    }

    public ID getID() {
        return this.userID;
    }

    class ClientFacade implements ClientListener {

        @Override
        public void onStateChanged(byte oldState, byte newState) {
            //#debug
            long before = 0;
            //#debug
            Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] onStateChanged( " + oldState + ", " + newState + ")");
            switch (newState) {
                case BaseClient.CONNECTED:
                    //#debug
                    before = System.currentTimeMillis();
                    //#debug
                    Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] GraffitiClient.ClientFacade.onStateChanged( , CONNECTED) took " + (System.currentTimeMillis() - before) + " ms");
                    break;

                case BaseClient.READY:
                    //#debug
                    before = System.currentTimeMillis();
                    TilesCache.getInstance().setUserID(baseclient.getMe().getId());
                    try {
                        // TODO load addressbook
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                    //#debug
                    Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] GraffitiClient.ClientFacade.onStateChanged( , READY) took " + (System.currentTimeMillis() - before) + " ms");
                    break;

                case BaseClient.OFFLINE:
                    //#debug
                    before = System.currentTimeMillis();
                    if (oldState == BaseClient.READY) {
                        try {
                            // TODO save addressbook
                            baseclient.getMe().getProfile().getAddressBook().reset();
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    } else if (oldState == BaseClient.STARTED) {
                    }
                    if (baseclient.isConnectionBrokenByDisconnectFromServer()) {
                    } else {
                    }
                    //#debug
                    Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] GraffitiClient.ClientFacade.onStateChanged( , OFFLINE) took " + (System.currentTimeMillis() - before) + " ms");
                    break;
            }
        }

        @Override
        public void onGraffitoCreation(GraffitoHeader header, GraffitoBody body) {
        }

        @Override
        public void onIncomingBody(GraffitoHeader header, GraffitoBody body) {
        }

        @Override
        public void onFriendFinderPermissionStateReply() {
        }

        @Override
        public void onFriendFinderPermissionUpdateReply() {
        }

        @Override
        public void onClientUpdateNotification(String msg, String updateUrl) {
        }

        @Override
        public void positionSystemChanged(PositionProvider oldSystem, PositionProvider newSystem) {
        }

        @Override
        public void spatialNameReply(SpatialNameList list) {
        }

        @Override
        public void WLANPositioningAvailable(WLANPosAvailable msg) {
        }

        @Override
        public void onError(Error error) {
            //#debug
            Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] GraffitiClient.onError");
            Debug.out.println("Error " + error.getErrorMessage());
        }
    }

    class PositionListenerImpl implements PositionListener {

            @Override
            public void onPositionChanged(Position _old, Position _new) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        }

    class AddressbookFacade implements AddressBookListener {

        @Override
        public void onPersonSearch(String searchValue, Person[] results) {
        }

        @Override
        public void onPersonLoaded(Person p) {
        }

        @Override
        public void incomingFriendRequest(final ID id, final String msg) {
            try {
                baseclient.getMe().getProfile().getAddressBook().get(id, new LoadPersonListener() {

                    @Override
                    public void onPersonLoaded(Person p) {
                    }
                });
            } catch (Exception ex) {
            }
        }
    }

    class GraffitiFacade implements GraffitiCacheListener {

        @Override
        public void addGraffito(GraffitoHeader graffito) {
        }

        @Override
        public void removeGraffito(GraffitoHeader graffito, boolean deleted) {
        }

        @Override
        public void updateGraffito(GraffitoHeader graffito) {
        }

        @Override
        public void popupGraffito(final GraffitoHeader graffito) {
            if (baseclient.getPositionProvider() == null || baseclient.getPositionProvider().getCurrentPosition() == null || (baseclient.getPositionProvider().getCurrentPosition() instanceof QualifiedPosition && ((QualifiedPosition) baseclient.getPositionProvider().getCurrentPosition()).getFloor() == graffito.getFloor())) {

                graffito.setPopupAlreadyShown(true);
            }
        }

        @Override
        public void bodyLoaded(GraffitoBody body) {
        }
    }

    class TimeoutFacade implements TicketTimeoutListener {

        @Override
        public void ticketTimedOut(Ticket ticket) {
            //#debug
            Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] ticket " + ticket.getTicketId() + " timed out!!");
            if (ticket.getMessage().getType() == LoginRequest.MessageTypeIDs.LOGIN_REQUEST) {
            }
        }
    }
}
